//----------------------------------------------------------------------------
// Project    : Pheidippides
// Company    : Cadence Design Systems
//----------------------------------------------------------------------------
// Description: 
// This file is a simple testbench for the AXI BFMs.
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// Required timescale.
//----------------------------------------------------------------------------
`timescale 1ns / 1ps

//----------------------------------------------------------------------------
// Local Defines.
//----------------------------------------------------------------------------

// Response Type Defines
`define RESPONSE_OKAY   2'b00
`define RESPONSE_EXOKAY 2'b01
`define RESPONSE_SLVERR 2'b10
`define RESPONSE_DECERR 2'b11

`define BURST_TYPE_FIXED 2'b00
`define BURST_TYPE_INCR  2'b01
`define BURST_TYPE_WRAP  2'b10
`define LOCK_TYPE_NORMAL    1'b0
`define LOCK_TYPE_EXCLUSIVE 1'b1

// AMBA AXI 4 Lite Range Constants
`define MAX_BURST_LENGTH 1
`define MAX_DATA_SIZE (DATA_BUS_WIDTH*`MAX_BURST_LENGTH)/8
   
// AMBA AXI 4 Lite Bus Size Constants
`define PROT_BUS_WIDTH 3
`define RESP_BUS_WIDTH 2

// Define for intenal control value
`define ADDRVALID_FALSE 0
`define ADDRVALID_TRUE  1

`define M1_DATA_BUS_WIDTH 32
`define M1_ID_BUS_WIDTH 1
`define M1_USER_BUS_WIDTH 1


module tb;
  parameter DATA_BUS_WIDTH = 32;
  import "DPI-C" context task StartSoftware();
  export "DPI-C" task RegReadByte;
  export "DPI-C" task RegReadWord;
  export "DPI-C" task RegReadDWord;
  export "DPI-C" task RegWriteByte;
  export "DPI-C" task RegWriteWord;
  export "DPI-C" task RegWriteDWord;
  export "DPI-C" task MemRead;
  export "DPI-C" task MemWrite;
  export "DPI-C" task MemReadDW;
  export "DPI-C" task MemWriteDW;
   
  reg tb_ACLK;
  reg tb_ARESETn;

   //------------------------------------------------------------------------
   // Add an instance of the AXI 4 LITE MASTER BFM
   //------------------------------------------------------------------------
//  axi4_lite_master 
  cdn_axi4_lite_master_bfm #(
    .NAME("master0"),
    .DATA_BUS_WIDTH(32),
    .ADDRESS_BUS_WIDTH(32),
    .MAX_OUTSTANDING_TRANSACTIONS(8))
  master0(
    .ACLK    (tb_ACLK),
    .ARESETn (tb_ARESETn),
    // Write Address Channel
    .AWADDR  (dut.S_AWADDR),
//    .AWPROT  (dut.S_AWPROT),
    .AWVALID (dut.S_AWVALID),
    .AWREADY (dut.S_AWREADY),
    // Write Data Channel Signals.
    .WDATA  (dut.S_WDATA),
    .WSTRB  (dut.S_WSTRB), 
    .WVALID (dut.S_WVALID),
    .WREADY (dut.S_WREADY),
    // Write Response Channel Signals.
    .BRESP  (dut.S_BRESP),
    .BVALID (dut.S_BVALID),
    .BREADY (dut.S_BREADY),
    // Read Address Channel Signals.
    .ARADDR  (dut.S_ARADDR),
//    .ARPROT  (dut.S_ARPROT),
    .ARVALID (dut.S_ARVALID),
    .ARREADY (dut.S_ARREADY),
    // Read Data Channel Signals.
    .RDATA  (dut.S_RDATA),
    .RRESP  (dut.S_RRESP),
    .RVALID (dut.S_RVALID),
    .RREADY (dut.S_RREADY));

  cdn_axi4_master_bfm #(
    .NAME               ("Axi4Master"),
    .DATA_BUS_WIDTH     (`M1_DATA_BUS_WIDTH),
    .ADDRESS_BUS_WIDTH  (32),
    .ID_BUS_WIDTH       (`M1_ID_BUS_WIDTH),
    .AWUSER_BUS_WIDTH   (`M1_USER_BUS_WIDTH),
    .ARUSER_BUS_WIDTH   (`M1_USER_BUS_WIDTH),
    .RUSER_BUS_WIDTH    (`M1_USER_BUS_WIDTH),
    .WUSER_BUS_WIDTH    (`M1_USER_BUS_WIDTH),
    .BUSER_BUS_WIDTH    (`M1_USER_BUS_WIDTH),
//    .STOP_ON_ERROR      (0),
//    .CHANNEL_LEVEL_INFO (0),
    .MAX_OUTSTANDING_TRANSACTIONS(8),
    .EXCLUSIVE_ACCESS_SUPPORTED(0))
  master1(
    .ACLK    (tb_ACLK),
    .ARESETn (tb_ARESETn),
    // Write Address Channel
    .AWID    (tbuf.S1_AXI_AWID),
    .AWADDR  (tbuf.S1_AXI_AWADDR),
    .AWLEN   (tbuf.S1_AXI_AWLEN),
    .AWSIZE  (tbuf.S1_AXI_AWSIZE),
    .AWBURST (tbuf.S1_AXI_AWBURST),
    .AWLOCK  (tbuf.S1_AXI_AWLOCK),
    .AWCACHE (tbuf.S1_AXI_AWCACHE),
    .AWPROT  (tbuf.S1_AXI_AWPROT),
//    .AWREGION(tbuf.S1_AXI_AWREGION),
    .AWQOS   (tbuf.S1_AXI_AWQOS),
//    .AWUSER  (tbuf.S1_AXI_AWUSER),
    .AWVALID (tbuf.S1_AXI_AWVALID),
    .AWREADY (tbuf.S1_AXI_AWREADY),
    // Write Data Channel Signals.
    .WDATA  (tbuf.S1_AXI_WDATA),
    .WSTRB  (tbuf.S1_AXI_WSTRB), 
    .WLAST  (tbuf.S1_AXI_WLAST), 
//    .WUSER  (tbuf.S1_AXI_WUSER),
    .WVALID (tbuf.S1_AXI_WVALID),
    .WREADY (tbuf.S1_AXI_WREADY),
    // Write Response Channel Signals.
    .BID    (tbuf.S1_AXI_BID),
    .BRESP  (tbuf.S1_AXI_BRESP),
//    .BUSER  (tbuf.S1_AXI_BUSER),
    .BVALID (tbuf.S1_AXI_BVALID),
    .BREADY (tbuf.S1_AXI_BREADY),
    // Read Address Channel Signals.
    .ARID    (tbuf.S1_AXI_ARID),
    .ARADDR  (tbuf.S1_AXI_ARADDR),
    .ARLEN   (tbuf.S1_AXI_ARLEN),
    .ARSIZE  (tbuf.S1_AXI_ARSIZE),
    .ARBURST (tbuf.S1_AXI_ARBURST),
    .ARLOCK  (tbuf.S1_AXI_ARLOCK),
    .ARCACHE (tbuf.S1_AXI_ARCACHE),
    .ARPROT  (tbuf.S1_AXI_ARPROT),
//    .ARREGION(tbuf.S1_AXI_ARREGION),
    .ARQOS   (tbuf.S1_AXI_ARQOS),
//    .ARUSER  (tbuf.S1_AXI_ARUSER),
    .ARVALID (tbuf.S1_AXI_ARVALID),
    .ARREADY (tbuf.S1_AXI_ARREADY),
    // Read Data Channel Signals.
    .RID    (tbuf.S1_AXI_RID),
    .RDATA  (tbuf.S1_AXI_RDATA),
    .RRESP  (tbuf.S1_AXI_RRESP),
    .RLAST  (tbuf.S1_AXI_RLAST),
//    .RUSER  (tbuf.S1_AXI_RUSER),
    .RVALID (tbuf.S1_AXI_RVALID),
    .RREADY (tbuf.S1_AXI_RREADY));
/*
wire [15:0] axis_master_TKEEP;
wire [15:0] axis_master_TSTRB;
wire [3:0]  dut_S_TSTRB = {axis_master_TKEEP[12], axis_master_TKEEP[8], axis_master_TKEEP[4], axis_master_TKEEP[0]};   
  pcie_stream_master #(
    .DATA_BUS_WIDTH(128))
  axis_master(
    .ACLK    (tb_ACLK),
    .ARESETn (tb_ARESETn),
    // Transfer Channel
    .TVALID (dut.S_TVALID),
    .TREADY (dut.S_TREADY),
    .TDATA  (dut.S_TDATA),
    .TSTRB  (axis_master_TSTRB),
    .TKEEP  (axis_master_TKEEP),
    .TLAST  (dut.S_TLAST));

  pcie_stream_slave #(
    .DATA_BUS_WIDTH(128))
  axis_slave(.ACLK    (tb_ACLK),
           .ARESETn (tb_ARESETn),
           // Transfer Channel
           .TVALID (dut.M_TVALID),
//           .TREADY (dut.M_TREADY),
           .TDATA  (dut.M_TDATA),
//           .TSTRB  (dut.M_TSTRB),
           .TKEEP  (dut.M_TSTRB),
           .TLAST  (dut.M_TLAST)
//           .TID    (dut.M_TID),
//           .TDEST  (dut.M_TDEST),
//           .TUSER  (dut.M_TUSER)
           );
*/   
  exerciser_module dut(
    .CLK        (tb_ACLK),
    .RST        (!tb_ARESETn),
  
    .S_AWADDR   (master0.AWADDR ),
    .S_AWVALID  (master0.AWVALID),
    .S_AWREADY  (master0.AWREADY),
    .S_WDATA    (master0.WDATA  ),
    .S_WSTRB    (master0.WSTRB  ),
    .S_WVALID   (master0.WVALID ),
    .S_WREADY   (master0.WREADY ),
    .S_BRESP    (master0.BRESP  ),
    .S_BVALID   (master0.BVALID ),
    .S_BREADY   (master0.BREADY ),
    .S_ARADDR   (master0.ARADDR ),
    .S_ARVALID  (master0.ARVALID),
    .S_ARREADY  (master0.ARREADY),
    .S_RDATA    (master0.RDATA  ),
    .S_RRESP    (master0.RRESP  ),
    .S_RVALID   (master0.RVALID ),
    .S_RREADY   (master0.RREADY ),

    .S_TVALID   (dut.M_TVALID),
    .S_TREADY   (dut.M_TREADY),
    .S_TDATA    (dut.M_TDATA ),
    .S_TSTRB    (dut.M_TSTRB ),
    .S_TUSER    (dut.M_TUSER ),
    .S_TLAST    (dut.M_TLAST ),
  
    .M_TVALID   (dut.S_TVALID),
    .M_TREADY   (dut.S_TREADY),
    .M_TDATA    (dut.S_TDATA ),
    .M_TSTRB    (dut.S_TSTRB ),
    .M_TUSER    (dut.S_TUSER ),
    .M_TLAST    (dut.S_TLAST ),

    .M0_AWID    (tbuf.S0_AXI_AWID),
    .M0_AWADDR  (tbuf.S0_AXI_AWADDR ),
    .M0_AWLEN   (tbuf.S0_AXI_AWLEN  ),
    .M0_AWSIZE  (tbuf.S0_AXI_AWSIZE ),
    .M0_AWBURST (tbuf.S0_AXI_AWBURST),
    .M0_AWVALID (tbuf.S0_AXI_AWVALID),
    .M0_AWREADY (tbuf.S0_AXI_AWREADY),
    .M0_WDATA   (tbuf.S0_AXI_WDATA  ),
    .M0_WSTRB   (tbuf.S0_AXI_WSTRB  ),
    .M0_WLAST   (tbuf.S0_AXI_WLAST  ),
    .M0_WVALID  (tbuf.S0_AXI_WVALID ),
    .M0_WREADY  (tbuf.S0_AXI_WREADY ),
    .M0_BID     (tbuf.S0_AXI_BID),
    .M0_BRESP   (tbuf.S0_AXI_BRESP  ),
    .M0_BVALID  (tbuf.S0_AXI_BVALID ),
    .M0_BREADY  (tbuf.S0_AXI_BREADY ),
    .M0_ARID    (tbuf.S0_AXI_ARID),
    .M0_ARADDR  (tbuf.S0_AXI_ARADDR ),
    .M0_ARLEN   (tbuf.S0_AXI_ARLEN  ),
    .M0_ARSIZE  (tbuf.S0_AXI_ARSIZE ),
    .M0_ARBURST (tbuf.S0_AXI_ARBURST),
    .M0_ARVALID (tbuf.S0_AXI_ARVALID),
    .M0_ARREADY (tbuf.S0_AXI_ARREADY),
    .M0_RID     (tbuf.S0_AXI_RID),
    .M0_RDATA   (tbuf.S0_AXI_RDATA  ),
    .M0_RRESP   (tbuf.S0_AXI_RRESP  ),
    .M0_RLAST   (tbuf.S0_AXI_RLAST  ),
    .M0_RVALID  (tbuf.S0_AXI_RVALID ),
    .M0_RREADY  (tbuf.S0_AXI_RREADY )
  );
  
  TraceBuffer
  tbuf (
    .CLK            (tb_ACLK),
    .RST            (!tb_ARESETn),
  
    .S0_AXI_AWID    (dut.M0_AWID),
    .S0_AXI_AWADDR  (dut.M0_AWADDR),
    .S0_AXI_AWLEN   (dut.M0_AWLEN),
    .S0_AXI_AWSIZE  (dut.M0_AWSIZE),
    .S0_AXI_AWBURST (dut.M0_AWBURST),
    .S0_AXI_AWLOCK  (1'b0),
    .S0_AXI_AWCACHE (4'b0),
    .S0_AXI_AWPROT  (3'b0),
    .S0_AXI_AWQOS   (4'b0),
    .S0_AXI_AWVALID (dut.M0_AWVALID),
    .S0_AXI_AWREADY (dut.M0_AWREADY),
    .S0_AXI_WDATA   (dut.M0_WDATA  ),
    .S0_AXI_WSTRB   (dut.M0_WSTRB  ),
    .S0_AXI_WLAST   (dut.M0_WLAST  ),
    .S0_AXI_WVALID  (dut.M0_WVALID ),
    .S0_AXI_WREADY  (dut.M0_WREADY ),
    .S0_AXI_BID     (dut.M0_BID),
    .S0_AXI_BRESP   (dut.M0_BRESP  ),
    .S0_AXI_BVALID  (dut.M0_BVALID ),
    .S0_AXI_BREADY  (dut.M0_BREADY ),
    .S0_AXI_ARID    (dut.M0_ARID),
    .S0_AXI_ARADDR  (dut.M0_ARADDR ),
    .S0_AXI_ARLEN   (dut.M0_ARLEN  ),
    .S0_AXI_ARSIZE  (dut.M0_ARSIZE ),
    .S0_AXI_ARBURST (dut.M0_ARBURST),
    .S0_AXI_ARLOCK  (1'b0),
    .S0_AXI_ARCACHE (4'b0),
    .S0_AXI_ARPROT  (3'b0),
    .S0_AXI_ARQOS   (4'b0),
    .S0_AXI_ARVALID (dut.M0_ARVALID),
    .S0_AXI_ARREADY (dut.M0_ARREADY),
    .S0_AXI_RID     (dut.M0_RID),
    .S0_AXI_RDATA   (dut.M0_RDATA  ),
    .S0_AXI_RRESP   (dut.M0_RRESP  ),
    .S0_AXI_RLAST   (dut.M0_RLAST  ),
    .S0_AXI_RVALID  (dut.M0_RVALID ),
    .S0_AXI_RREADY  (dut.M0_RREADY ),
  
    .S1_AXI_AWID    (master1.AWID   ),
    .S1_AXI_AWADDR  (master1.AWADDR ),
    .S1_AXI_AWLEN   (master1.AWLEN  ),
    .S1_AXI_AWSIZE  (master1.AWSIZE ),
    .S1_AXI_AWBURST (master1.AWBURST),
    .S1_AXI_AWLOCK  (master1.AWLOCK ),
    .S1_AXI_AWCACHE (master1.AWCACHE),
    .S1_AXI_AWPROT  (master1.AWPROT ),
    .S1_AXI_AWQOS   (master1.AWQOS  ),
    .S1_AXI_AWVALID (master1.AWVALID),
    .S1_AXI_AWREADY (master1.AWREADY),
    .S1_AXI_WDATA   (master1.WDATA  ),
    .S1_AXI_WSTRB   (master1.WSTRB  ),
    .S1_AXI_WLAST   (master1.WLAST  ),
    .S1_AXI_WVALID  (master1.WVALID ),
    .S1_AXI_WREADY  (master1.WREADY ),
    .S1_AXI_BID     (master1.BID    ),
    .S1_AXI_BRESP   (master1.BRESP  ),
    .S1_AXI_BVALID  (master1.BVALID ),
    .S1_AXI_BREADY  (master1.BREADY ),
    .S1_AXI_ARID    (master1.ARID   ),
    .S1_AXI_ARADDR  (master1.ARADDR ),
    .S1_AXI_ARLEN   (master1.ARLEN  ),
    .S1_AXI_ARSIZE  (master1.ARSIZE ),
    .S1_AXI_ARBURST (master1.ARBURST),
    .S1_AXI_ARLOCK  (master1.ARLOCK ),
    .S1_AXI_ARCACHE (master1.ARCACHE),
    .S1_AXI_ARPROT  (master1.ARPROT ),
    .S1_AXI_ARQOS   (master1.ARQOS  ),
    .S1_AXI_ARVALID (master1.ARVALID),
    .S1_AXI_ARREADY (master1.ARREADY),
    .S1_AXI_RID     (master1.RID    ),
    .S1_AXI_RDATA   (master1.RDATA  ),
    .S1_AXI_RRESP   (master1.RRESP  ),
    .S1_AXI_RLAST   (master1.RLAST  ),
    .S1_AXI_RVALID  (master1.RVALID ),
    .S1_AXI_RREADY  (master1.RREADY )
    );


   //------------------------------------------------------------------------
   // Include Test Level API
   //------------------------------------------------------------------------
   `include "test_api.v"

  initial 
  begin
    tb_ACLK = 1'b0;
    tb_ARESETn = 1'b1;
    #10 tb_ARESETn = 1'b0;
    #50;
    // Release the reset on the posedge of the clk.
    @(posedge tb_ACLK);
    tb_ARESETn = 1'b1;
    @(posedge tb_ACLK);
    StartSoftware;
   end

  initial 
  begin
    // Wait for end of reset
    wait(tb_ARESETn === 0) @(posedge tb.tb_ACLK);
    wait(tb_ARESETn === 1) @(posedge tb.tb_ACLK);
    #30000
    $finish;
  end

  always #5 tb_ACLK = !tb_ACLK;

task RegReadByte;
  input int Address;
  output byte Data;
  reg [31:0] Value;
  reg [1:0] Response;
  begin
    master0.READ_BURST(Address, 3'b0, Value, Response);
    CHECK_RESPONSE_OKAY(Response);
    case (Address[1:0])
      2'b00: Data = Value[7:0];
      2'b01: Data = Value[15:8];
      2'b10: Data = Value[23:16];
      2'b11: Data = Value[31:24];
    endcase
  end
endtask

task RegReadWord;
  input int Address;
  output shortint Data;
  reg [31:0] Value;
  reg [1:0] Response;
  begin
    if (Address[0])
    begin
      $display("Address is not Word Aligned");
      $stop;
    end
    master0.READ_BURST(Address, 3'b0, Value, Response);
    CHECK_RESPONSE_OKAY(Response);
    case (Address[0])
      1'b0: Data = Value[15:0];
      1'b1: Data = Value[31:16];
    endcase
  end
endtask

task RegReadDWord;
  input int Address;
  output int Data;
  reg [31:0] Value;
  reg [1:0] Response;
  begin
    if (Address[1:0])
    begin
      $display("Address is not DWord Aligned");
      $stop;
    end
    master0.READ_BURST(Address, 3'b0, Value, Response);
    CHECK_RESPONSE_OKAY(Response);
    Data = Value;
  end
endtask

task RegWriteByte;
  input int Address;
  input byte Data;
  reg [1:0] Response;
  begin
    master0.WRITE_BURST_CONCURRENT(Address, 3'b0, Data, 1, Response);
    CHECK_RESPONSE_OKAY(Response);
  end
endtask

task RegWriteWord;
  input int Address;
  input shortint Data;
  reg [1:0] Response;
  begin
    if (Address[0])
    begin
      $display("Address is not Word Aligned");
      $stop;
    end
    master0.WRITE_BURST_CONCURRENT(Address, 3'b0, Data, 2, Response);
    CHECK_RESPONSE_OKAY(Response);
  end
endtask

task RegWriteDWord;
  input int Address;
  input int Data;
  reg [1:0] Response;
  begin
    if (Address[1:0])
    begin
      $display("Address is not DWord Aligned");
      $stop;
    end
    master0.WRITE_BURST_CONCURRENT(Address, 3'b0, Data, 4, Response);
    CHECK_RESPONSE_OKAY(Response);
  end
endtask

  localparam BurstSize =  (`M1_DATA_BUS_WIDTH == 8) ? 3'b000 :
                          (`M1_DATA_BUS_WIDTH == 16) ? 3'b001 :
                          (`M1_DATA_BUS_WIDTH == 32) ? 3'b010 :
                          (`M1_DATA_BUS_WIDTH == 64) ? 3'b011 :
                          (`M1_DATA_BUS_WIDTH == 128) ? 3'b100 :
                          (`M1_DATA_BUS_WIDTH == 256) ? 3'b101 :
                          (`M1_DATA_BUS_WIDTH == 512) ? 3'b110 : 3'b111;
                          
task MemRead;
  input int Address;
  input int Bytes;
  output byte Data[4096];
  reg [8191 : 0] RData;
  reg [`M1_USER_BUS_WIDTH * 256 - 1 : 0] RUser;
  reg [1:0] Response;
  int Remaining;
  int Length;
  int i, j;
begin
  Remaining = Bytes/4;
  j=0;
  while (Remaining)
  begin
    Length = (Remaining < 256) ? Remaining : 256;
    master1.READ_BURST(0, Address, Length - 1, 3'b010, `BURST_TYPE_INCR, `LOCK_TYPE_NORMAL,
                     0, 0, 0, 0, 0, RData, Response, RUser);
    CHECK_RESPONSE_OKAY(Response);
    Address += (Length * 4);
    Remaining -= Length;
    for (i=0; i<4*Length; i++, j++)
    begin
      Data[j] = RData[8*i+:8];
    end
  end
end
endtask

task MemWrite;
  input int Address;
  input int Bytes;
  input int Data[1024];
  integer WDataSize;
  reg [32767 : 0] WData;
  reg [`M1_USER_BUS_WIDTH * 256 - 1 : 0] WUser;
  reg [`M1_USER_BUS_WIDTH * 256 - 1 : 0] BUser;
  reg [1:0] Response;
  int Remaining;
  int Length;
  int i, j;
begin
  Remaining = Bytes/4;
  j=0;
  while (Remaining)
  begin
    Length = (Remaining < 256) ? Remaining : 256;
    for(i=0; i<4*Length; i++, j++)
      WData[32*i+:32] = Data[j];
    master1.WRITE_BURST(0, Address, Length - 1, 3'b010, `BURST_TYPE_INCR, `LOCK_TYPE_NORMAL,
                      0, 0, WData, 4 * Length, 0, 0, 0, WUser, Response, BUser);
    CHECK_RESPONSE_OKAY(Response);
    Address += 4*Length;
    Remaining -= Length;
  end
end
endtask

task MemReadDW;
  input int Address;
  output int Data;
  reg [8191 : 0] RData;
  reg [`M1_USER_BUS_WIDTH * 256 - 1 : 0] RUser;
  reg [1:0] Response;
begin
  master1.READ_BURST(0, Address, 0, 3'b010, `BURST_TYPE_INCR, `LOCK_TYPE_NORMAL,
                     0, 0, 0, 0, 0, RData, Response, RUser);
  CHECK_RESPONSE_OKAY(Response);
  Data = RData[31:0];
end
endtask

task MemWriteDW;
  input int Address;
  input int Data;
  reg [8191 : 0] WData;
  reg [`M1_USER_BUS_WIDTH * 256 - 1 : 0] WUser;
  reg [`M1_USER_BUS_WIDTH * 256 - 1 : 0] BUser;
  reg [1:0] Response;
begin
  WData[31:0] = Data;
  master1.WRITE_BURST(0, Address, 0, 3'b010, `BURST_TYPE_INCR, `LOCK_TYPE_NORMAL,
                      0, 0, WData, 4, 0, 0, 0, WUser, Response, BUser);
  CHECK_RESPONSE_OKAY(Response);
end
endtask
   
endmodule

//----------------------------------------------------------------------------
// END OF FILE
//----------------------------------------------------------------------------
